/** @file q3_bsp.h
    @brief Quake 3 BSP object */

#ifndef G3_Q3BSP_H
#define G3_Q3BSP_H

#include <string.h>
#include <vector>
#include <set>

#include "res/res_texture_mgr.h"

#include <GL/gl.h>

#include "color.h"
#include "scenegraph.h"
#include "file/f_stream.h"
#include "math/m_plane.h"
#include "math/m_bounding.h"
#include "math/m_intersection.h"
#include "render/r_frustum.h"

using std::string;
using std::vector;
using std::set;

namespace G3 {

enum Q3BSP_FACE_TYPES {
	Q3BSPF_Polygon = 1,
	Q3BSPF_Patch,
	Q3BSPF_Mesh,
	Q3BSPF_Billboard
};

enum Q3BSP_TYPES {
	Q3BSPL_Entities = 0,
	Q3BSPL_Textures,
	Q3BSPL_Planes,
	Q3BSPL_Nodes,
	Q3BSPL_Leaves,
	Q3BSPL_LeafFaces,
	Q3BSPL_LeafBrushes,
	Q3BSPL_Models,
	Q3BSPL_Brushes,
	Q3BSPL_BrushSides,
	Q3BSPL_Vertices,
	Q3BSPL_MeshIndices,
	Q3BSPL_Effects,
	Q3BSPL_Faces,
	Q3BSPL_Lightmaps,
	Q3BSPL_LightVols,
	Q3BSPL_VisData
};

enum Q3BSP_CONTENT_FLAGS {
	Q3BSPCF_Solid = 1
};

enum Q3BSP_INTERSECTION_FLAGS {
	Q3BSPIF_StartsOut = 1,
	Q3BSPIF_EndsOut = 2,
	Q3BSPIF_AllSolid = 4
};

extern string Q3BSP_LUMPNAMES [17];

// #pragma pack(1) is not supported by all compilers
// so, we just don't use it. We manually read in all class members

// Lump classes
class Q3BSP_LUMP {
	public:
		Q3BSP_LUMP ();

		int FileOffset;
		int FileLength;

		Q3BSP_LUMP& operator=(const Q3BSP_LUMP &l);

		long GetSize ();

		bool Read (STREAM *Stream, LOG *Log);

		void Clear ();
};

class Q3BSP_HEADER {
	public:
		Q3BSP_HEADER ();

		char Signature [4];
		int Version;

		Q3BSP_LUMP Lumps [17];

		Q3BSP_HEADER& operator=(const Q3BSP_HEADER &h);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);

		void Clear ();
};

class Q3BSP_LUMP_ENTITIES {
	public:
		Q3BSP_LUMP_ENTITIES ();

		int Size;
		char *pEntities;

		Q3BSP_LUMP_ENTITIES& operator=(const Q3BSP_LUMP_ENTITIES &e);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);

		void Clear ();
};

class Q3BSP_LUMP_TEXTURE {
	public:
		Q3BSP_LUMP_TEXTURE ();

		char Name [64];
		// Type of surface
		int SurfaceFlags;
		// Leaf content
		int ContentFlags;

		// Texture Handle
		H_TEXTURE Handle;

		Q3BSP_LUMP_TEXTURE& operator=(const Q3BSP_LUMP_TEXTURE &t);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_PLANE {
	public:
		Q3BSP_LUMP_PLANE ();

		// Plane Normal
		float Normal [3];
		// Distance from plane to origin
		float Dist;

		Q3BSP_LUMP_PLANE& operator=(const Q3BSP_LUMP_PLANE &p);

		PLANE3F GetPlane ();
		void SetPlane (PLANE3F &p);
		VEC3F GetNormal ();

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_NODE {
	public:
		Q3BSP_LUMP_NODE ();

		int Plane;
		int Children [2];

		// Reads in 6 integers and then converts into floats
		float BBox [6];
		//int BBox [6];

		Q3BSP_LUMP_NODE& operator=(const Q3BSP_LUMP_NODE &n);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_LEAF {
	public:
		Q3BSP_LUMP_LEAF ();

		// Visibility Cluster
		int Cluster;
		// Bot Navigation Area
		int Area;

		// Reads in 6 integers and then converts into floats
		float BBox [6];
		//int BBox [6];

		// Lookup for the face list (indexes are for faces)
		int FirstFace;
		int NumFaces;

		// Lookup for the brush list (indexes are for brushes)
		int FirstBrush;
		int NumBrushes;

		Q3BSP_LUMP_LEAF& operator=(const Q3BSP_LUMP_LEAF &l);

		long GetSize ();

		G3::AABB3F GetBBox ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_LEAF_FACE {
	public:
		Q3BSP_LUMP_LEAF_FACE ();

		int FaceIndex;

		Q3BSP_LUMP_LEAF_FACE& operator= (const Q3BSP_LUMP_LEAF_FACE &lf);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_LEAF_BRUSH {
	public:
		Q3BSP_LUMP_LEAF_BRUSH ();

		int BrushIndex;

		Q3BSP_LUMP_LEAF_BRUSH& operator=(const Q3BSP_LUMP_LEAF_BRUSH &lb);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_MODEL {
	public:
		Q3BSP_LUMP_MODEL ();

		float BBox [6];

		int FirstFace;
		int NumFaces;

		int FirstBrush;
		int NumBrushes;

		Q3BSP_LUMP_MODEL& operator=(const Q3BSP_LUMP_MODEL &m);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_BRUSH {
	public:
		Q3BSP_LUMP_BRUSH ();

		int FirstSide;
		int NumSides;

		int TexIndex;

		Q3BSP_LUMP_BRUSH& operator=(const Q3BSP_LUMP_BRUSH &b);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_BRUSH_SIDE {
	public:
		Q3BSP_LUMP_BRUSH_SIDE ();

		int PlaneNum;

		int TexIndex;

		Q3BSP_LUMP_BRUSH_SIDE& operator=(const Q3BSP_LUMP_BRUSH_SIDE &bs);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_VERTEX {
	public:
		Q3BSP_LUMP_VERTEX ();

		float Point [3];

		float TexCoord [2];
		// Lightmap texture coordinates
		float LightTexCoord [2];

		float Normal [3];

		// Vertex color
		uint Color;

		Q3BSP_LUMP_VERTEX& operator=(const Q3BSP_LUMP_VERTEX &lv);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);

		G3::COL4D GetColor ();
};

class Q3BSP_LUMP_MESH_VERTEX {
	public:
		Q3BSP_LUMP_MESH_VERTEX ();

		// Vertex index offset, relative to first vertex of corresponding face.
		int Offset;

		Q3BSP_LUMP_MESH_VERTEX& operator=(const Q3BSP_LUMP_MESH_VERTEX &mv);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_EFFECT {
	public:
		Q3BSP_LUMP_EFFECT ();

		char Name [64];
		int Brush;
		int Unknown;

		Q3BSP_LUMP_EFFECT& operator=(const Q3BSP_LUMP_EFFECT &e);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_FACE {
	public:
		Q3BSP_LUMP_FACE ();

		int ShaderIndex;
		// If no effect, then -1
		int EffectIndex;
		// 1 - polygon, 2 - patch, 3 - mesh, 4 - billboard
		int FaceType;

		int FirstVert;
		int NumVerts;

		// Every 3 meshverts describe a triangle
		int FirstMeshVerts;
		int NumMeshVerts;

		int LightmapIndex;
		// X,Y Corner of this face's lightmap image in the lightmap
		int LightmapStart [2];
		int LightmapSize [2];

		// Worldspace origin of the lightmap
		float LightmapOrigin [3];
		// Worldspace lightmap s and t unit vectors
		float LightmapVects [2][3];

		float Normal [3];

		int PatchSize [2];

		Q3BSP_LUMP_FACE& operator=(const Q3BSP_LUMP_FACE &f);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_LIGHTMAP {
	public:
		Q3BSP_LUMP_LIGHTMAP ();

		byte Map [128][128][3];

		Q3BSP_LUMP_LIGHTMAP& operator=(const Q3BSP_LUMP_LIGHTMAP &l);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);
};

class Q3BSP_LUMP_LIGHTVOL {
	public:
		Q3BSP_LUMP_LIGHTVOL ();

		byte Ambient [3];
		byte Directional [3];
		// 0 - phi, 1 - theta
		byte Direction [2];

		Q3BSP_LUMP_LIGHTVOL& operator=(const Q3BSP_LUMP_LIGHTVOL &lv);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Dump (G3::LOG *Log);
};

class Q3BSP_LUMP_VISIBILITY {
	public:
		Q3BSP_LUMP_VISIBILITY ();

		int NumClusters;
		int BytesPerCluster;
		byte *Buffer;

		Q3BSP_LUMP_VISIBILITY& operator=(const Q3BSP_LUMP_VISIBILITY &v);

		long GetSize ();

		bool Read (G3::STREAM *Stream, G3::LOG *Log);

		void Clear ();

		void Dump (G3::LOG *Log);
};

// BSP class
class Q3BSP_NODE: public OBJECT_NODE {
	public:
		Q3BSP_NODE ();
		~Q3BSP_NODE ();

		Q3BSP_HEADER Header;
		Q3BSP_LUMP_ENTITIES Entities;
		vector<Q3BSP_LUMP_TEXTURE> Textures;
		vector<Q3BSP_LUMP_PLANE> Planes;
		vector<Q3BSP_LUMP_NODE> Nodes;
		vector<Q3BSP_LUMP_LEAF> Leaves;
		vector<Q3BSP_LUMP_LEAF_FACE> LeafFaces;
		vector<Q3BSP_LUMP_LEAF_BRUSH> LeafBrushes;
		vector<Q3BSP_LUMP_MODEL> Models;
		vector<Q3BSP_LUMP_BRUSH> Brushes;
		vector<Q3BSP_LUMP_BRUSH_SIDE> BrushSides;
		vector<Q3BSP_LUMP_VERTEX> Vertices;
		vector<Q3BSP_LUMP_MESH_VERTEX> MeshVertices;
		vector<Q3BSP_LUMP_EFFECT> Effects;
		vector<Q3BSP_LUMP_FACE> Faces;
		vector<Q3BSP_LUMP_LIGHTMAP> Lightmaps;
		vector<Q3BSP_LUMP_LIGHTVOL> LightVols;
		Q3BSP_LUMP_VISIBILITY VisData;

		set<int> VisibleFaces;
		int LastLeaf;

        // Debugging
        bool RenderBBoxes;
		set<int> LeavesInFrustum;
		set<int> LeavesSpanFrustum;

		INTERSECTION3F LastIntersection;
		int LastIntersectionBrush;
		int LastIntersectionStartPlane;
		int LastIntersectionEndPlane;
		VEC3F LastRayStart;
		VEC3F LastRayEnd;

		LOG *Log;

		bool UsePVS;
		bool UseFrustumCulling;
		bool RenderColorArrays;
		bool RenderTexCoordArrays;

		bool ReadHeader (STREAM *Stream);
		bool ReadEntities (STREAM *Stream);
		bool ReadTextures (STREAM *Stream);
		bool ReadPlanes (STREAM *Stream);
		bool ReadNodes (STREAM *Stream);
		bool ReadLeaves (STREAM *Stream);
		bool ReadLeafFaces (STREAM *Stream);
		bool ReadLeafBrushes (STREAM *Stream);
		bool ReadModels (STREAM *Stream);
		bool ReadBrushes (STREAM *Stream);
		bool ReadBrushSides (STREAM *Stream);
		bool ReadVertices (STREAM *Stream);
		bool ReadMeshVertices (STREAM *Stream);
		bool ReadEffects (STREAM *Stream);
		bool ReadFaces (STREAM *Stream);
		bool ReadLightmaps (STREAM *Stream);
		bool ReadLightVols (STREAM *Stream);
		bool ReadVisData (STREAM *Stream);

		bool Load (STREAM *Stream);

		void Dump ();

//==============================================================================
// Finds the leaf the Position lies in
//==============================================================================
		int FindLeaf (VEC3F Position);

//==============================================================================
// Tests if a cluster is visible from another
//==============================================================================
		bool IsClusterVisible (int visCluster, int testCluster);

//==============================================================================
// Finds all faces that are visible from the Position
//==============================================================================
		int FindVisibleFaces (FRUSTUM &ViewFrustum);

//==============================================================================
// Simple intersection functions
//==============================================================================
		void CheckNode (int nID, float StartF, float EndF, VEC3F &Start, VEC3F &End);
		void CheckBrush (Q3BSP_LUMP_BRUSH *Brush);
		INTERSECTION3F& TraceRay (VEC3F &Start, VEC3F &End);

//==============================================================================
// Renders a face
//
//	TODO:: Optimize heavily
//	TODO:: Patch rendering
//==============================================================================
		void RenderFace (int fID = 0, uint RenderMode = GL_TRIANGLES, float ElapsedTime = 1.0);

		void Render (float ElapsedTime = 1.0);
		void Update (float ElapsedTime = 1.0);

//==============================================================================
// Scales the BSP
//
//	TODO:: Scale entities aswell
//==============================================================================
		void Scale (float Factor);

//==============================================================================
// Converts the BSP axis (X -> East, Y -> South, Z -> Down)
// into (X -> East, Y -> Up, Z -> South)
//
//	TODO:: Convert entities aswell
//==============================================================================
		void ConvertAxis ();

//==============================================================================
// Converts float vectors from orientation (X -> East, Y -> South, Z -> Down)
// into (X -> East, Y -> Up, Z -> South)
//==============================================================================
		void ConvertVectorAxis (float *v);
};

}

#endif
